/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __AUDIO_OPENAL_H__
#define __AUDIO_OPENAL_H__

#pragma once
#include "audio/iaudio.h"
#include "audio/isoundbuffer.h"
#include "audio/AbstractSound_OpenAL.h"
#include "al/al.h"
#include "al/alc.h"
#include <vector>


namespace openAL {

class SoundEffect_OpenAL;

class DLL_EXPORT Audio_OpenAL :
	public audio::IAudio
{
public:
	Audio_OpenAL(void);
	virtual ~Audio_OpenAL(void);

	/**
	*	Clean up. Cleans all ISounds and IStreamingSounds.
	*/
	virtual void Destroy();

	/**
	*	Init the audio engine. 
	*	With 3D support sounds are placeble in space
	*	and listenerPosition is in sync with the camera.
	*/
	virtual bool init(bool b3D);

	/*
	*	Queries the system for usable sound devices:
	*/
	void getAllSoundDevices();

	/*
	* Returns the list containing all device names
	*/
	std::vector<std::string> getDeviceList();

	/*
	*	Returns the name of the selected (and used) device
	*/
	std::string getCurrentDeviceName();

	/*
	*	tries to use the default device.
	*	Returns false if opening the device fails
	*/
	bool useDefaultDevice();

	/*
	*	if opening the default sound device fails, try to use
	* the first sound device found
	*/
	bool useFirstSoundDevice();

	/*
	*	try to open the sound device named "name"
	*/
	ALCdevice* openSoundDevice( std::string name);

	/*
	*	try to create a context on the device "device"
	*/
	bool createContext(ALCdevice* device);

	
	/*
	* DEPRICATED!
	* The sound device is always created, if the init() returns true
	* Later, the sound device can be changed from the ( to be created) sound menu!
	*/
	bool changeDevice(std::string deviceName);
	
	/**
	*	The update loop.
	*/
	virtual void update(double dt);

	/**
	*	Create an ISound by name. Sounds are not loaded twice.
	*	\param fileName Name of soundfile.
	*	\return ISound The created ISound
	*/
	virtual audio::ISound* createISound(const std::string &sFileName);

	/**
	*	Create an IStreamingSound by name. Sounds are not loaded twice.
	*	\param fileName Name of soundfile.
	*	\return ISound The created ISound
	*/
	virtual audio::ISound* createIStreamingSound(const std::string &fileName);

	/**
	*	De-register ISound from audio-engine
	*	Is called from destructor of ISound!
	*/
	virtual void destroyISound(audio::ISound* _pSound, float _fadeTime=0.1f);

	/**
	*	Create and destroy an IJukeBox
	*/
	virtual audio::IJukeBox* createIJukeBox();
	virtual void destroyIJukeBox(audio::IJukeBox* _jb);


	/**
	*	Check if there is an available source and return it
	*/
	ALuint requestSource(AbstractSound_OpenAL* _sound, int _priority);

	/**
	* Make a short fade out before stopping a sound.
	* !!! Short fadeout is performed but _fFadeTime is ignored at the moment !!! 
	*/
	virtual void stopISound(audio::ISound* _pSound, float _fFadeTime = 0);
			
protected:

	struct SourceObject{
		ALuint sourceID;	///< SourceID
		bool bInUse;		///< Indicates if the sound is in use by an ISound
		
		float fFadeFactor;	///< Only used in handleSoundsToStop
		AbstractSound_OpenAL* source_sound;
	};

	/**                                                                         
	*	Create an ISoundBuffer from a Soundfile
	*	Supported formats (hopefully):
	*	-wav
	*	-aiff
	*	-ogg	
	*/
	virtual audio::ISoundBuffer* createSoundBufferFromFile(const std::string &fileName);
	
	/**
	*	Sync position of the listener with the camera position
	*/
	void updateListener();

	/**
	*	Does an initialisation of the Listener for 2D use!
	*	Position is	(0,0,0)
	*	viewVec is	(0,0,1)
	*	upVec is	(0,1,0)
	*/
	void initListender2D();

	/**
	*	Call the update()-function of all ISound
	*/
	void  updateSources(const double &dt);

	/**
	*	Encapsultes all paramters that are need for the distacnce model
	*/
	void setDistanceModel();

	/**
	*	Call update() of all sounds in m_SoundUpdateList ans clear list
	*/
	void updateSoundsAndClearList(const double& dt);

	/**
	*	Fade out sounds and delete them afterwards
	*/
	void handleDeleteSounds(double dt);

	/**
	*	Fade out sounds
	*/
	void handleStopSounds(double dt);

	ALCcontext *m_pContext;
	ALCdevice *m_pDevice;
	std::string m_DeviceName;
	std::vector<std::string> m_vAllDeviceNames;

	std::map<ALuint, SourceObject*>m_mSourceObjects; ///< Vector of all sources

	std::map<void*, audio::ISound*>m_mISounds; ///< Vector off all ISounds

	std::map<AbstractSound_OpenAL*,AbstractSound_OpenAL*>m_SoundUpdateList;
	std::map<ALuint,SourceObject*>m_SoundsToStop;
	std::map<ALuint,SourceObject*>m_SoundsToDestroy;
	
	int m_iNumberOfSources; ///< Number of available sources
	int m_iFreeSources; ///< Number of free sources (is updated regularly)

	bool m_b3D_ENABLED;

	ALuint m_JukeBoxSource[2]; ///< Ensure that at least one JukeBox gets a sourceID
};


} //namespace

#endif __AUDIO_OPENAL_H__
